package org.zxl.springbootdemo.springjdbc2.config;


import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.interceptor.*;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class TxAnoConfig {
    private static final int TX_METHOD_TIMEOUT = 5;
    private static final String AOP_POINTCUT_EXPRESSION = "execution (* org.zxl.springbootdemo.springjdbc2.service.*.*(..))";

    @Autowired
    private PlatformTransactionManager transactionManager;

    /**
     * 以下是事物的传播机制：
     * @Transactional(propagation=Propagation.REQUIRED)
     * 如果有事务, 那么加入事务, 没有的话新建一个(默认情况下)
     * @Transactional(propagation=Propagation.NOT_SUPPORTED)
     * 容器不为这个方法开启事务
     * @Transactional(propagation=Propagation.REQUIRES_NEW)
     * 不管是否存在事务, 都创建一个新的事务, 原来的挂起, 新的执行完毕, 继续执行老的事务
     * @Transactional(propagation=Propagation.MANDATORY)
     * 必须在一个已有的事务中执行, 否则抛出异常
     * @Transactional(propagation=Propagation.NEVER)
     * 必须在一个没有的事务中执行, 否则抛出异常(与Propagation.MANDATORY相反)
     * @Transactional(propagation=Propagation.SUPPORTS)
     * 如果其他bean调用这个方法, 在其他bean中声明事务, 那就用事务.如果其他bean没有声明事务, 那就不用事务.
     *
     */

    @Bean
    public TransactionInterceptor txAdvice(){
        NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();
        /*只读，容器不为这个方法开启事务*/
        RuleBasedTransactionAttribute readOnlyTx = new RuleBasedTransactionAttribute();
        readOnlyTx.setReadOnly(true);
        readOnlyTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED);

        /*如果有事务, 那么加入事务, 没有的话新建一个(默认情况下)*/
        RuleBasedTransactionAttribute  requiredTx = new RuleBasedTransactionAttribute();
        //遇到指定类型异常时回滚
        requiredTx.setRollbackRules(
                Collections.singletonList(new RollbackRuleAttribute(Exception.class)));
        requiredTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        //事务超时时间
        requiredTx.setTimeout(TX_METHOD_TIMEOUT);
        Map<String, TransactionAttribute> txMap = new HashMap<>();
        txMap.put("add*", requiredTx);
        txMap.put("save*", requiredTx);
        txMap.put("insert*", requiredTx);
        txMap.put("update*", requiredTx);
        txMap.put("delete*", requiredTx);
        txMap.put("get*", readOnlyTx);
        txMap.put("query*", readOnlyTx);
        source.setNameMap(txMap);
        TransactionInterceptor txAdvice = new TransactionInterceptor(transactionManager, source);
        return txAdvice;
    }

    @Bean
    public Advisor txAdviceAdvisor(){
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression(AOP_POINTCUT_EXPRESSION);
        return new DefaultPointcutAdvisor(pointcut, txAdvice());
    }


    /**等同于与xml中的配置
     *
     * <!-- 配置事务管理器 -->
     *     <bean id = "txManage" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
     *         <property name="dataSource" ref="dataSource"></property>
     *     </bean>
     *
     *     <!--配置事务传播特性 -->
     *     <tx:advice id = "txAdvice" transaction-manager = "txManage">
     *         <tx:attributes>
     *             <tx:method name="get" propagation = "REQUIRED" read-only="true"/>
     *             <tx:method name="update" propagation = "REQUIRED"/>
     *             <tx:method name="delete" propagation = "REQUIRED"/>
     *             <tx:method name="insert" propagation = "REQUIRED"/>
     *             <tx:method name="*" propagation = "REQUIRED"/>
     *         </tx:attributes>
     *     </tx:advice>
     *
     *     <aop:config>
     *         <aop:pointcut expression="execution(* org.zxl.springdemo.mvc.repository.*.*(..))" id="pointcut"/>
     *         <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
     *     </aop:config>
     */
}
